﻿using Abp.Application.Services.Dto;
using Abp.Authorization.Users;
using Abp.Domain.Repositories;
using Abp.Organizations;
using Abp.AutoMapper;
using Xugege.AbpExtension.Application.Extension.Organizations.Dto;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Linq.Dynamic;
using Abp.Extension.Schedule.Core.Entitys;
using Abp.Extensions;
using Abp.Linq.Extensions;
using Abp.Runtime.Caching;
using Abp.Threading;
using Abp.UI;
using Xugege.AbpExtension.Authorization.Users;
using Xugege.AbpExtension.Authorization.Users.Dto;
using Xugege.AbpExtension.Organizations.Dto;

namespace Xugege.AbpExtension.Application.Extension.Organizations
{
    public class OrganizationUnitExtensionService : AbpZeroTemplateAppServiceBase, IOrganizationUnitExtensionService
    {

        private readonly OrganizationUnitManager _organizationUnitManager;
        private readonly IRepository<OrganizationUnit, long> _organizationUnitRepository;
        private readonly IRepository<UserOrganizationUnit, long> _userOrganizationUnitRepository;
        private readonly IRepository<User, long> _useRepository;
        private readonly ICacheManager _cacheManager;
        private readonly ICache _userCache;
        private readonly ICache _orgUnitCache;
        private readonly IRepository<SchedulingEntity, long> _schedulingRepository;
        private readonly IRepository<ShiftsEntity, long> _shiftsRepository;

        public OrganizationUnitExtensionService(
            ICacheManager cacheManager,
            OrganizationUnitManager organizationUnitManager,
            IRepository<OrganizationUnit, long> organizationUnitRepository,
            IRepository<UserOrganizationUnit, long> userOrganizationUnitRepository,
            IRepository<User, long> useRepository,
            IRepository<SchedulingEntity, long> schedulingRepository,
            IRepository<ShiftsEntity, long> shiftsRepository)
        {
            _cacheManager = cacheManager;
            _organizationUnitManager = organizationUnitManager;
            _organizationUnitRepository = organizationUnitRepository;
            _userOrganizationUnitRepository = userOrganizationUnitRepository;
            _useRepository = useRepository;
            _schedulingRepository = schedulingRepository;
            _shiftsRepository = shiftsRepository;

            _userCache = cacheManager.GetCache(ApplicationExtensionConsts.UserListCacheName);
            _orgUnitCache = cacheManager.GetCache(ApplicationExtensionConsts.OrgUnitListCacheName);
        }

        public async Task<PagedResultDto<OrganizationUnitUserListsDto>> GetOrganizationUnitUsers(GetOrganizationUnitExtensionInput input)
        {

            var tenant = await GetCurrentTenantAsync();

            var query = from uou in _userOrganizationUnitRepository.GetAll()
                        join ou in _organizationUnitRepository.GetAll().Where(m => m.TenantId == tenant.Id) on uou.OrganizationUnitId equals ou.Id
                        join user in UserManager.Users on uou.UserId equals user.Id
                        where uou.OrganizationUnitId == input.Id
                        select new { uou, user };

            var totalCount = await query.CountAsync();
            var items = await query.OrderBy(input.Sorting).PageBy(input).ToListAsync();

            return new PagedResultDto<OrganizationUnitUserListsDto>(
                totalCount,
                items.Select(item =>
                {
                    var dto = item.user.MapTo<OrganizationUnitUserListsDto>();
                    dto.AddedTime = item.uou.CreationTime;
                    return dto;
                }).ToList());
        }

        /// <summary>
        /// 获取租户下所有的部门
        /// </summary>
        /// <returns></returns>
        public async Task<IList<OrganizationUnitDto>> GetAllOrgs()
        {
            var t = await GetCurrentTenantAsync();
            var userOrgUnits = await _userOrganizationUnitRepository.GetAll().ToListAsync();
            var result = (await _organizationUnitRepository.GetAllListAsync(m => m.TenantId == t.Id))
                .MapTo<IList<OrganizationUnitDto>>();
            foreach (var organizationUnitDto in result)
            {
                organizationUnitDto.MemberCount = userOrgUnits.Count(m => m.OrganizationUnitId == organizationUnitDto.Id);
            }
            return result;
        }

        /// <summary>
        /// 获取租户下所有的用户信息
        /// </summary>
        /// <returns></returns>
        public async Task<IList<UserListDto>> GetAllUsers()
        {
            var t = await GetCurrentTenantAsync();

            var users = (await _useRepository.GetAll()
                    .Where(u => u.TenantId == t.Id)
                    .Include("Roles")
                    .ToListAsync())
                .MapTo<IList<UserListDto>>();
            //foreach (var user in users)
            //{
            //    var orgUnits = await _userOrganizationUnitRepository.GetAll().Where(m => m.UserId == user.Id).ToListAsync();
            //    user.OrgUnits = orgUnits.MapTo<List<OrganizationUnitDto>>();
            //}
            return users;
        }

        public async Task<IList<UserListDto>> GetUserList()
        {
            IList<UserListDto> result = new List<UserListDto>();
            var t = await GetCurrentTenantAsync();
            var userIds = await _useRepository.GetAll().Where(u => u.TenantId == t.Id).Select(m => m.Id).ToListAsync();
            foreach (var userId in userIds)
            {
                result.Add(_userCache.Get(userId.ToString(), () =>
                {
                    var r = AsyncHelper.RunSync(() => GetUserById(userId));
                    _userCache.Set(userId.ToString(), r);
                    return r;
                }));
            }
            return result;
        }

        private async Task<UserListDto> GetUserById(long id)
        {
            var tenantId = GetCurrentTenant().Id;
            var user = await _useRepository.GetAllIncluding().FirstOrDefaultAsync(m => m.Id == id);
            if (user == null) return null;
            var userDto = user.MapTo<UserListDto>();
            var orgUnits = await _userOrganizationUnitRepository.GetAll().Where(m => m.UserId == user.Id).Select(m => m.OrganizationUnitId).ToListAsync();
            var orgs = await _organizationUnitRepository.GetAll().Where(m => m.TenantId == tenantId && orgUnits.Contains(m.Id)).ToListAsync();
            userDto.OrgUnits = orgs.MapTo<List<OrganizationUnitDto>>();

            return userDto;
        }

        /// <summary>
        /// 获取用户信息 包含在组织单位中
        /// </summary>
        /// <returns></returns>
        public async Task<IList<OrganizationUnitWithUserOutputDto>> GetOrganizationUnitsWithUsers()
        {
            var tenantId = GetCurrentTenant().Id;
            IList<OrganizationUnitWithUserOutputDto> result = new List<OrganizationUnitWithUserOutputDto>();

            var orgIds = await _organizationUnitRepository.GetAll().Where(m => m.TenantId == tenantId).Select(m => m.Id).ToListAsync();
            foreach (var orgId in orgIds)
            {
                var orgDto = _orgUnitCache.Get(orgId, () =>
                {
                    var org = AsyncHelper.RunSync(() => GetOrganizationUnitWithUserByOrgId(orgId));
                    _userCache.Set(orgId.ToString(), org);
                    return org;
                });
                result.Add(orgDto);
            }

            return result;
        }
        /// <summary>
        /// 获取组织机构 根据用户
        /// </summary>
        /// <param name="input">用户id 如果不传递则使用当前登录用户</param>
        /// <returns></returns>
        public async Task<IList<OrganizationUnitDto>> GetOrganizationUnitByUser(GetOrganizationUnitByUserInputDto input)
        {

            var tenantId = GetCurrentTenant().Id;

            //1. 查询用户是否存在
            var user = await _useRepository.FirstOrDefaultAsync(input.UserId == 0 ? GetCurrentUser().Id : input.UserId);
            if (user == null)
            {
                throw new UserFriendlyException(L("NoUser"));
            }
            //2. 查询用户所在部门
            var userOrgUnits = await _userOrganizationUnitRepository.GetAll().ToListAsync();
            var userOfUnits = userOrgUnits.Where(m => m.UserId == user.Id).Select(m => m.OrganizationUnitId).ToList();

            //3. 查询部门
            var orgUnits = await _organizationUnitRepository.GetAll().Where(m => m.TenantId == tenantId && userOfUnits.Contains(m.Id)).ToListAsync();
            var result = orgUnits.MapTo<List<OrganizationUnitDto>>();
            foreach (var organizationUnit in orgUnits)
            {
                //organizationUnit.ParentId = null;
                var dto = result.FirstOrDefault(m => m.Id == organizationUnit.Id);
                if (dto != null) dto.ParentId = null;
                var children = await _organizationUnitManager.FindChildrenAsync(organizationUnit.Id, true);
                result = result.Concat(children.MapTo<List<OrganizationUnitDto>>()).ToList();
            }
            foreach (var organizationUnitDto in result)
            {
                organizationUnitDto.MemberCount = userOrgUnits.Count(m => m.OrganizationUnitId == organizationUnitDto.Id);
            }
            return result;
        }

        /// <summary>
        /// 获取部门下的用户
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<IList<UserListDto>> GetUserListByOrgId(GetUserListByOrgIdInputDto input)
        {
            //获取部门下的用户id
            var userIds = await _userOrganizationUnitRepository.GetAll().Where(m => m.OrganizationUnitId == input.OrgUnitId).Select(m => m.UserId).ToListAsync();
            var users = await _useRepository.GetAll().Where(m => userIds.Contains(m.Id)).ToListAsync();
            return users.MapTo<List<UserListDto>>();
        }
        /// <summary>
        /// 获取部门下的用户
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<IList<UserListWithScheduleDto>> GetUserListWithScheduleByOrgId(GetUserListByOrgIdInputDto input)
        {
            //获取部门下的用户id
            //var userIds = await _userOrganizationUnitRepository.GetAll().Where(m => m.OrganizationUnitId == input.OrgUnitId).Select(m => m.UserId).ToListAsync();
            //var users = await _useRepository.GetAll().Where(m => userIds.Contains(m.Id)).ToListAsync();
            var tenantId = GetCurrentTenant().Id;
            var query = from user in _useRepository.GetAll()
                        join uo in _userOrganizationUnitRepository.GetAll().Where(m => m.TenantId == tenantId && m.OrganizationUnitId == input.OrgUnitId)
                            on user.Id equals uo.UserId
                        join schedule in _schedulingRepository.GetAll() on user.Id equals schedule.UserId into scheduleTemp
                        from schedule2 in scheduleTemp.DefaultIfEmpty()
                        join shifts in _shiftsRepository.GetAll() on schedule2.ShiftId equals shifts.Id into shiftsTemp
                        from shifts2 in shiftsTemp.DefaultIfEmpty()
                        select new { user, shifts2.BeginTime, shifts2.EndTime };
            var result = (await query.ToListAsync()).Select(m =>
            {
                var r = m.user.MapTo<UserListWithScheduleDto>();
                r.WorkTime = !m.BeginTime.IsNullOrEmpty() && !m.EndTime.IsNullOrEmpty() ? m.BeginTime + "-" + m.EndTime : "--待定--";
                return r;
            }).ToList();

            return result;
        }


        private async Task<OrganizationUnitWithUserOutputDto> GetOrganizationUnitWithUserByOrgId(long id)
        {
            var org = await _organizationUnitRepository.FirstOrDefaultAsync(id);
            if (org == null) return null;

            OrganizationUnitWithUserOutputDto result = org.MapTo<OrganizationUnitWithUserOutputDto>();
            var query = from uou in _userOrganizationUnitRepository.GetAll()
                        join ou in _organizationUnitRepository.GetAll() on uou.OrganizationUnitId equals ou.Id
                        join user in UserManager.Users on uou.UserId equals user.Id
                        where uou.OrganizationUnitId == id
                        select new { uou, user };
            var users = await query.OrderBy(m => m.user.Id).Select(m => m.user).ToListAsync();
            result.Users = users.MapTo<IList<UserOutputDto>>();
            return result;
        }
    }
}
